#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "cutils.h"
#include "iomem.h"

static PhysMemoryRange *default_register_ram (PhysMemoryMap *s, uint64_t addr,
                                      uint64_t size, int devram_flags);

static PhysMemoryRange *default_register_device (PhysMemoryMap *s, uint64_t addr,
                                        uint64_t size, void * opaque,
                                        DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
                                        int devio_flags);

PhysMemoryMap *
phys_mem_map_init () {
    PhysMemoryMap *s;
    s = mallocz (sizeof (*s));
    s->register_device = default_register_device;
    s->register_ram = default_register_ram;
    return s;
}


static PhysMemoryRange *
default_register_ram (PhysMemoryMap *s, uint64_t addr,
                                      uint64_t size, int devram_flags) {
    assert (s->n_phys_mem_range < PHYS_MEM_RANGE_MAX);
    PhysMemoryRange *pr;
    pr = &s->phys_mem_range[s->n_phys_mem_range++];
    pr->map = s;
    pr->addr = addr;
    pr->size = size;
    pr->is_ram = TRUE;
    pr->phys_mem = mallocz (size);
    pr->devram_flags = devram_flags;
    return pr;
}

static PhysMemoryRange *
default_register_device (PhysMemoryMap *s, uint64_t addr,
                                        uint64_t size, void * opaque,
                                        DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
                                        int devio_flags) {
    assert (s->n_phys_mem_range < PHYS_MEM_RANGE_MAX);
    PhysMemoryRange *pr;
    pr = &s->phys_mem_range[s->n_phys_mem_range++];
    pr->map = s;
    pr->addr = addr;
    pr->size = size;
    pr->is_ram = FALSE;
    pr->read_func = read_func;
    pr->write_func = write_func;
    pr->opaque = opaque;
    pr->devio_flags = devio_flags;
    return pr;
}

PhysMemoryRange *
iomem_get_phys_mem_range (PhysMemoryMap *s, uint64_t paddr) {
    PhysMemoryRange *pr;
    for (pr = s->phys_mem_range; pr < s->phys_mem_range + s->n_phys_mem_range; pr++) {
        if (paddr >= pr->addr && paddr < pr->addr + pr->size)
            return pr;
    }
    return NULL;
}

uint8_t *
iomem_get_phys_mem_ptr (PhysMemoryMap *s, uint64_t paddr) {
    PhysMemoryRange *pr = iomem_get_phys_mem_range (s, paddr);
    uintptr_t offset;
    if (!pr || !pr->is_ram)
        return NULL;
    offset = paddr - pr->addr;
    return pr->phys_mem + (uintptr_t)offset;
}

void
irq_init(IRQSignal *irq, SetIRQFunc *set_irq, void *opaque, int irq_num) {
    irq->set_irq = set_irq;
    irq->opaque = opaque;
    irq->irq_num = irq_num;
}
